`include "timescale.v"
`include "uart_defines.v"

`define UART_DL1 7:0
`define UART_DL2 15:8

module uart_regs (
	clk,
	rst,
	cs,
	wr,
	rd,
	read_addr,
	write_addr,
	data_in,
	data_out,
	int_o,

	stx_o,
	srx_i
	);

input			clk;
input		    rst;
input			cs;
input			wr;
input			rd;
input	[7:0]	read_addr;
input	[7:0]	write_addr;
input	[7:0]	data_in;
output	[7:0]	data_out;

output			stx_o;
input			srx_i;

output			int_o;

reg 										enable;

reg		[7:0]	data_out;

reg [2:0] 								ier;
reg [3:0] 								iir;
wire									tffull;	//iir bit4
reg 	 								fcr;  /// bits 7 of fcr.1 for tftl, 0 for rftl
reg [7:0] 								lcr;
reg [15:0] 								dl;  // 32-bit divisor latch
reg	[7:0]								rftl;	//rx fifo trigger level register
reg	[7:0]								tftl;	//tx fifo trigger level register

reg 									start_dlc; // activate dlc on writing to UART_DL1
reg 									lsr_mask_d; // delay for lsr_mask condition
//reg 										threi_clear; // THRE interrupt clear flag
reg [15:0] 								dlc;  // 32-bit divisor latch counter
reg 										int_o;

reg [7:0] 								rx_trigger_level; // trigger level of the receiver FIFO
reg 										rx_reset;
reg 										tx_reset;

wire 										dlab;			   // divisor latch access bit
wire										srx_pad;
wire										loopback;

// LSR bits wires and regs
wire [7:0] 								lsr;
wire 										lsr0, lsr1, lsr2, lsr3, lsr4, lsr5, lsr6, lsr7;
reg										lsr0r, lsr1r, lsr2r, lsr3r, lsr4r, lsr5r, lsr6r, lsr7r;
wire 										lsr_mask; // lsr_mask

//
// ASSINGS
//

assign 									lsr[7:0] = { lsr7r, lsr6r, lsr5r, lsr4r, lsr3r, lsr2r, lsr1r, lsr0r };
assign 									dlab = lcr[`UART_LC_DL]; //lcr.7

// Interrupt signals
wire 										rls_int;  // receiver line status interrupt
wire 										rda_int;  // receiver data available interrupt
wire 										ti_int;   // timeout indicator interrupt
wire										thre_int; // transmitter holding register empty interrupt

// FIFO signals
wire 										tf_push;
wire 										rf_pop;
wire 			[`UART_FIFO_REC_WIDTH-1:0] 	rf_data_out;	//width 11 because break,parity,framing error bits
wire 										rf_error_bit; // an error (parity or framing) is inside the fifo
wire 			[`UART_FIFO_COUNTER_W-1:0] 	rf_count;
wire 			[`UART_FIFO_COUNTER_W-1:0] 	tf_count;
wire 			[2:0] 						tstate;
wire 			[3:0] 						rstate;
wire 			[9:0] 						counter_t;

wire                      thre_set_en; // THRE status is delayed one character time when a character is written to fifo.
wire					  tfll_set_en;
reg  [7:0]                block_cnt;   // While counter counts, THRE status is blocked (delayed one character cycle)
reg  [7:0]                block_value; // One character length minus stop bit
reg	 [7:0]				  trigger_block_cnt;

// Transmitter Instance
wire serial_out;

uart_transmitter transmitter(
		.clk(clk),
		.rst(rst),
		.lcr(lcr),
		.tf_push(tf_push),
		.data_in(data_in),
		.enable(enable),
		.stx_o(serial_out),
		.tstate(tstate),
		.tf_count(tf_count),
		.tx_reset(tx_reset),
		.lsr_mask(lsr_mask)
	);

  // Synchronizing and sampling serial RX input
  uart_sync_flops    i_uart_sync_flops
  (
    .rst_i           (rst),
    .clk_i           (clk),
    .stage1_rst_i    (1'b0),
    .stage1_clk_en_i (1'b1),
    .async_dat_i     (srx_i),
    .sync_dat_o      (srx_pad)
  );
  defparam i_uart_sync_flops.width      = 1;
  defparam i_uart_sync_flops.init_value = 1'b1;

// handle loopback
assign loopback = 1'b0;		//no loopback 
wire serial_in = loopback ? serial_out : srx_pad;
assign stx_o = loopback ? 1'b1 : serial_out;

// Receiver Instance
uart_receiver receiver(
		.clk(clk),
		.rst(rst),
		.lcr(lcr),
		.rf_pop(rf_pop),
		.srx_i(serial_in),
		.enable(enable),
		.counter_t(counter_t),
		.rf_count(rf_count),
		.rf_data_out(rf_data_out),
		.rf_error_bit(rf_error_bit),
		.rf_overrun(rf_overrun),
		.rx_reset(rx_reset),
		.lsr_mask(lsr_mask),
		.rstate(rstate),
		.rf_push_pulse(rf_push_pulse)
);


// Asynchronous reading here because the outputs are sampled in uart_wb.v file 
always @(dl or dlab or ier or iir 
			or lcr or lsr or rf_data_out or read_addr or rd)   // asynchrounous reading
begin
	if( cs & rd )
	begin
		case (read_addr)
			`UART_REG_RB    : data_out = dlab ? dl[`UART_DL1] : rf_data_out[10:3];
			`UART_REG_IE	: data_out = dlab ? dl[`UART_DL2] : {5'b00000,ier};
			`UART_REG_II	: data_out = {3'b110,tffull,iir};
			`UART_REG_LC	: data_out = lcr;
			`UART_REG_LS	: data_out = lsr;
			`UART_REG_TL	: data_out = fcr ? tftl : rftl;
			default:  data_out = 8'b0; // ??
		endcase // case(read_addr)
	end
	else
		data_out = 0;	
end // always @ (dl or dlab or ier or iir ...


// rf_pop signal handling
wire	rf_pop_tmp;
reg		rf_pop_tmp_d;

assign rf_pop_tmp = cs && rd && (read_addr == `UART_REG_RB && !dlab);
always @ (posedge clk or posedge rst)
begin
  if (rst)
  	rf_pop_tmp_d <= #1 1'b0;
  else
  	rf_pop_tmp_d <= #1 rf_pop_tmp;
end

assign rf_pop = rf_pop_tmp & ~rf_pop_tmp_d;

//always @(posedge clk or posedge rst)
//begin
//	if (rst)
//		rf_pop <= #1 0; 
//	else
//	if (rf_pop)	// restore the signal to 0 after one clock cycle
//		rf_pop <= #1 0;
//	else
//	if (rd && read_addr == `UART_REG_RB && !dlab)
//		rf_pop <= #1 1; // advance read pointer
//end

wire 	lsr_mask_condition;
wire 	iir_read;
wire	fifo_read;
wire	fifo_write;

assign lsr_mask_condition = (cs && rd && read_addr == `UART_REG_LS && !dlab);
assign iir_read = (cs && rd && read_addr == `UART_REG_II && !dlab);
assign fifo_read = (cs && rd && read_addr == `UART_REG_RB && !dlab);
assign fifo_write = (cs && wr && write_addr == `UART_REG_TR && !dlab);

// lsr_mask_d delayed signal handling
always @(posedge clk or posedge rst)
begin
	if (rst)
		lsr_mask_d <= #1 0;
	else // reset bits in the Line Status Register
		lsr_mask_d <= #1 lsr_mask_condition;
end

// lsr_mask is rise detected
assign lsr_mask = lsr_mask_condition && ~lsr_mask_d;

//
//   WRITES AND RESETS   //
//
// Line Control Register
always @(posedge clk or posedge rst)
	if (rst)
		lcr <= #1 8'b00000011; // 8n1 setting
	else
	if (cs && wr && write_addr==`UART_REG_LC)
		lcr <= #1 data_in;

// Interrupt Enable Register or UART_DL2
always @(posedge clk or posedge rst)
	if (rst)
	begin
		ier <= #1 3'b000; // no interrupts after reset
		dl[`UART_DL2] <= #1 8'b0;
	end
	else
	if (cs && wr && write_addr==`UART_REG_IE)
		if (dlab)
		begin
			dl[`UART_DL2] <= #1 data_in;
		end
		else
			ier <= #1 data_in[2:0]; // ier uses only 4 lsb


// FIFO Control Register and rx_reset, tx_reset signals
always @(posedge clk or posedge rst)
	if (rst) begin
		fcr <= #1 1'b0; 
		rx_reset <= #1 0;
		tx_reset <= #1 0;
	end else
	if (cs && wr && write_addr==`UART_REG_FC) begin
		fcr <= #1 data_in[7];
		rx_reset <= #1 data_in[1];
		tx_reset <= #1 data_in[2];
	end else begin
		rx_reset <= #1 0;
		tx_reset <= #1 0;
	end

// RX & TX FIFO Trigger Level
always @(posedge clk or posedge rst)
begin
	if (rst) 
	begin
		rftl <= #1 8'hF0;
		tftl <= #1 8'h00;
	end 
	else if (cs && wr && write_addr==`UART_REG_TL)	//for rftl
		if(fcr)	//for tftl
			tftl <= #1 data_in;
		else
			rftl <= #1 data_in;
end
	


// TX_FIFO or UART_DL1
always @(posedge clk or posedge rst)
	if (rst)
	begin
		dl[`UART_DL1]  <= #1 8'b0;
		//tf_push   <= #1 1'b0;
		start_dlc <= #1 1'b0;
	end
	else
	if (cs && wr && write_addr==`UART_REG_TR)
		if (dlab)
		begin
			dl[`UART_DL1] <= #1 data_in;
			start_dlc <= #1 1'b1; // enable DL counter
			//tf_push <= #1 1'b0;
		end
		else
		begin
			//tf_push   <= #1 1'b1;
			start_dlc <= #1 1'b0;
		end // else: !if(dlab)
	else
	begin
		start_dlc <= #1 1'b0;
		//tf_push   <= #1 1'b0;
	end // else: !if(dlab)

assign tf_push =  cs && wr && (write_addr == `UART_REG_TR) && !dlab;

// Receiver FIFO trigger level selection logic (asynchronous mux)
always @(*)
begin
	rx_trigger_level = rftl;
end
//always @(fcr)
//	case (fcr[`UART_FC_TL])
//		2'b00 : trigger_level = 1;
//		2'b01 : trigger_level = 4;
//		2'b10 : trigger_level = 8;
//		2'b11 : trigger_level = 14;
//	endcase // case(fcr[`UART_FC_TL])
	
//
//  STATUS REGISTERS  //
//
// Line Status Register

// activation conditions
assign lsr0 = (rf_count==0 && rf_push_pulse);  // data in receiver fifo available set condition
assign lsr1 = rf_overrun;     // Receiver overrun error
assign lsr2 = rf_data_out[1]; // parity error bit
assign lsr3 = rf_data_out[0]; // framing error bit
assign lsr4 = rf_data_out[2]; // break error in the character
//assign lsr5 = (tf_count==5'b0 && thre_set_en);  // transmitter fifo is empty
assign lsr5 = (tf_count <= {1'b0,tftl} && tfll_set_en); //transmitter fifo lower than tftl
assign lsr6 = (tf_count==5'b0 && thre_set_en && (tstate == /*`S_IDLE */ 0)); // transmitter empty
assign lsr7 = rf_error_bit | rf_overrun;

// lsr bit0 (receiver data available)
reg 	 lsr0_d;

always @(posedge clk or posedge rst)
	if (rst) lsr0_d <= #1 0;
	else lsr0_d <= #1 lsr0;

always @(posedge clk or posedge rst)
	if (rst) lsr0r <= #1 0;
	else lsr0r <= #1 (rf_count==1 && rf_pop && !rf_push_pulse || rx_reset) ? 0 : // deassert condition
					  lsr0r || (lsr0 && ~lsr0_d); // set on rise of lsr0 and keep asserted until deasserted 

// lsr bit 1 (receiver overrun)
reg lsr1_d; // delayed

always @(posedge clk or posedge rst)
	if (rst) lsr1_d <= #1 0;
	else lsr1_d <= #1 lsr1;

always @(posedge clk or posedge rst)
	if (rst) lsr1r <= #1 0;
	else	lsr1r <= #1	lsr_mask ? 0 : lsr1r || (lsr1 && ~lsr1_d); // set on rise

// lsr bit 2 (parity error)
reg lsr2_d; // delayed

always @(posedge clk or posedge rst)
	if (rst) lsr2_d <= #1 0;
	else lsr2_d <= #1 lsr2;

always @(posedge clk or posedge rst)
	if (rst) lsr2r <= #1 0;
	else lsr2r <= #1 lsr_mask ? 0 : lsr2r || (lsr2 && ~lsr2_d); // set on rise

// lsr bit 3 (framing error)
reg lsr3_d; // delayed

always @(posedge clk or posedge rst)
	if (rst) lsr3_d <= #1 0;
	else lsr3_d <= #1 lsr3;

always @(posedge clk or posedge rst)
	if (rst) lsr3r <= #1 0;
	else lsr3r <= #1 lsr_mask ? 0 : lsr3r || (lsr3 && ~lsr3_d); // set on rise

// lsr bit 4 (break indicator)
reg lsr4_d; // delayed

always @(posedge clk or posedge rst)
	if (rst) lsr4_d <= #1 0;
	else lsr4_d <= #1 lsr4;

always @(posedge clk or posedge rst)
	if (rst) lsr4r <= #1 0;
	else lsr4r <= #1 lsr_mask ? 0 : lsr4r || (lsr4 && ~lsr4_d);

// lsr bit 5 (transmitter fifo is empty)
reg lsr5_d;

always @(posedge clk or posedge rst)
	if (rst) lsr5_d <= #1 1;
	else lsr5_d <= #1 lsr5;

always @(posedge clk or posedge rst)
	if (rst) lsr5r <= #1 1;
	else lsr5r <= #1 (fifo_write) ? 0 :  lsr5r || (lsr5 && ~lsr5_d);

// lsr bit 6 (transmitter empty indicator)
reg lsr6_d;

always @(posedge clk or posedge rst)
	if (rst) lsr6_d <= #1 1;
	else lsr6_d <= #1 lsr6;

always @(posedge clk or posedge rst)
	if (rst) lsr6r <= #1 1;
	else lsr6r <= #1 (fifo_write) ? 0 : lsr6r || (lsr6 && ~lsr6_d);

// lsr bit 7 (error in fifo)
reg lsr7_d;

always @(posedge clk or posedge rst)
	if (rst) lsr7_d <= #1 0;
	else lsr7_d <= #1 lsr7;

always @(posedge clk or posedge rst)
	if (rst) lsr7r <= #1 0;
	else lsr7r <= #1 lsr_mask ? 0 : lsr7r || (lsr7 && ~lsr7_d);

// Frequency divider
always @(posedge clk or posedge rst) 
begin
	if (rst)
		dlc <= #1 0;
	else
		if (start_dlc | ~ (|dlc))
  			dlc <= #1 dl - 1;               // preset counter
		else
			dlc <= #1 dlc - 1;              // decrement counter
end

// Enable signal generation logic
always @(posedge clk or posedge rst)
begin
	if (rst)
		enable <= #1 1'b0;
	else
		if (|dl & ~(|dlc))     // dl>0 & dlc==0
			enable <= #1 1'b1;
		else
			enable <= #1 1'b0;
end

// Delaying THRE status for one character cycle after a character is written to an empty fifo.
always @(lcr)
  case (lcr[3:0])
    4'b0000                             : block_value =  95; // 6 bits
    4'b0100                             : block_value = 103; // 6.5 bits
    4'b0001, 4'b1000                    : block_value = 111; // 7 bits
    4'b1100                             : block_value = 119; // 7.5 bits
    4'b0010, 4'b0101, 4'b1001           : block_value = 127; // 8 bits
    4'b0011, 4'b0110, 4'b1010, 4'b1101  : block_value = 143; // 9 bits
    4'b0111, 4'b1011, 4'b1110           : block_value = 159; // 10 bits
    4'b1111                             : block_value = 175; // 11 bits
  endcase // case(lcr[3:0])

// Counting time of one character minus stop bit
always @(posedge clk or posedge rst)
begin
  if (rst)
    block_cnt <= #1 8'd0;
  else
  if(lsr5r & fifo_write)  // THRE bit set & write to fifo occured
    block_cnt <= #1 block_value;
  else
  if (enable & block_cnt != 8'b0)  // only work on enable times
    block_cnt <= #1 block_cnt - 1;  // decrement break counter
end // always of break condition detection

// Generating THRE status enable signal
assign thre_set_en = ~(|block_cnt);


always @(posedge clk or posedge rst)
begin
  if (rst)
    trigger_block_cnt <= #1 8'd0;
  else if( (tf_count == tftl) & fifo_write)
    trigger_block_cnt <= #1 block_value;
  else if (enable & trigger_block_cnt != 8'b0)
    trigger_block_cnt <= #1 trigger_block_cnt - 1;
end

assign tfll_set_en = ~(|trigger_block_cnt);

//
//	INTERRUPT LOGIC
//

assign rls_int  = ier[`UART_IE_RLS] && (lsr[`UART_LS_OE] || lsr[`UART_LS_PE] || lsr[`UART_LS_FE] || lsr[`UART_LS_BI]);
assign rda_int  = ier[`UART_IE_RDA] && (rf_count >= {1'b0,rx_trigger_level});
assign thre_int = ier[`UART_IE_THRE] && lsr[`UART_LS_TFE];
assign ti_int   = ier[`UART_IE_RDA] && (counter_t == 10'b0) && (|rf_count);

reg 	 rls_int_d;
reg 	 thre_int_d;
reg 	 ti_int_d;
reg 	 rda_int_d;

// delay lines
always  @(posedge clk or posedge rst)
	if (rst) rls_int_d <= #1 0;
	else rls_int_d <= #1 rls_int;

always  @(posedge clk or posedge rst)
	if (rst) rda_int_d <= #1 0;
	else rda_int_d <= #1 rda_int;

always  @(posedge clk or posedge rst)
	if (rst) thre_int_d <= #1 0;
	else thre_int_d <= #1 thre_int;

always  @(posedge clk or posedge rst)
	if (rst) ti_int_d <= #1 0;
	else ti_int_d <= #1 ti_int;

// rise detection signals

wire 	 rls_int_rise;
wire 	 thre_int_rise;
wire 	 ti_int_rise;
wire 	 rda_int_rise;

assign rda_int_rise    = rda_int & ~rda_int_d;
assign rls_int_rise 	  = rls_int & ~rls_int_d;
assign thre_int_rise   = thre_int & ~thre_int_d;
assign ti_int_rise 	  = ti_int & ~ti_int_d;

// interrupt pending flags
reg 	rls_int_pnd;
reg	rda_int_pnd;
reg 	thre_int_pnd;
reg 	ti_int_pnd;

// interrupt pending flags assignments
always  @(posedge clk or posedge rst)
	if (rst) rls_int_pnd <= #1 0; 
	else 
		rls_int_pnd <= #1 lsr_mask ? 0 :  						// reset condition
							rls_int_rise ? 1 :						// latch condition
							rls_int_pnd && ier[`UART_IE_RLS];	// default operation: remove if masked

always  @(posedge clk or posedge rst)
	if (rst) rda_int_pnd <= #1 0; 
	else 
		rda_int_pnd <= #1 ((rf_count == {1'b0,rx_trigger_level}) && fifo_read) ? 0 :  	// reset condition
							rda_int_rise ? 1 :						// latch condition
							rda_int_pnd && ier[`UART_IE_RDA];	// default operation: remove if masked

always  @(posedge clk or posedge rst)
	if (rst) thre_int_pnd <= #1 0; 
	else 
		thre_int_pnd <= #1 (tf_count == {1'b0,tftl} && fifo_write) || (iir_read & ~iir[`UART_II_IP] & iir[`UART_II_II] == `UART_II_THRE)? 0 : 
							thre_int_rise ? 1 :
							thre_int_pnd && ier[`UART_IE_THRE];

always  @(posedge clk or posedge rst)
	if (rst) ti_int_pnd <= #1 0; 
	else 
		ti_int_pnd <= #1 fifo_read ? 0 : 
							ti_int_rise ? 1 :
							ti_int_pnd && ier[`UART_IE_RDA];
// end of pending flags

// INT_O logic
always @(posedge clk or posedge rst)
begin
	if (rst)	
		int_o <= #1 1'b0;
	else
		int_o <= #1 
					rls_int_pnd		?	~lsr_mask					:
					rda_int_pnd		? 1								:
					ti_int_pnd		? ~fifo_read					:
					thre_int_pnd	? 1 :
					0;	// if no interrupt are pending
end


// Interrupt Identification register
always @(posedge clk or posedge rst)
begin
	if (rst)
		iir <= #1 1;
	else
	if (rls_int_pnd)  // interrupt is pending
	begin
		iir[`UART_II_II] <= #1 `UART_II_RLS;	// set identification register to correct value
		iir[`UART_II_IP] <= #1 1'b0;		// and clear the IIR bit 0 (interrupt pending)
	end else // the sequence of conditions determines priority of interrupt identification
	if (rda_int)
	begin
		iir[`UART_II_II] <= #1 `UART_II_RDA;
		iir[`UART_II_IP] <= #1 1'b0;
	end
	else if (ti_int_pnd)
	begin
		iir[`UART_II_II] <= #1 `UART_II_TI;
		iir[`UART_II_IP] <= #1 1'b0;
	end
	else if (thre_int_pnd)
	begin
		iir[`UART_II_II] <= #1 `UART_II_THRE;
		iir[`UART_II_IP] <= #1 1'b0;
	end else	// no interrupt is pending
	begin
		iir[`UART_II_II] <= #1 0;
		iir[`UART_II_IP] <= #1 1'b1;
	end
end

assign	tffull = (tf_count == 8'd256);

endmodule
